<?php

namespace MemFrameWork;

use \MemLib\Pdb;
use \MemLib\Log;

/**
 * mem short summary.
 *
 * 缓存基类
 *
 * @version 1.0
 * @author 张云江
 */
abstract class Mem
{
    /**
     * 请求的key
     */
    const memKey = "____MEMKEY____";
    /**
     * 键集合
     * @var array
     */
    private $keyarray;
    /**
     * 视图关系key,
     * 例如:视图v_doctor_base_info包含:t_doctor_department_info,t_doctor_hospital_info,t_doctor_info三个表,使用这个对象进行关联.
     * 存储过程pr_recure_dt_add引用以下表进行操作t_appointment_auxiliary_check,t_doctor_appointment_dt,t_doctor_appointment_dt,使用构造中的方式进行关联
     * 视图:视图名称作为键,关联表作为关联值
     * 存储过程:过程内被操作的表作为键,该存储过程作为关联值
     * @var array
     */
    private $viewrelatkey;
    /**
     * 数据库名
     * @var string
     */
    private $_dbname;
    /**
     * 数据表名
     * @var mixed
     */
    private $_tablename;
    /**
     * 构造,数据初始化
     * @param array $viewrelatkey 关系表
     * @param string $dbname 数据库名
     * @param string $tablename 数据表名
     */
    public function __construct($viewrelatkey, $dbname, $tablename)
    {
        $this->lock_on = true;
        $this->keyarray = array();
        $this->viewrelatkey = $viewrelatkey;
        $this->_dbname = $dbname;
        $this->_tablename = $tablename;
    }
    public function __destruct()
    {
        $this->close();
    }
    /**
     * 组装key前缀
     * @param string $dbname 数据库名称
     * @param string $tablename 表名
     * @param string $key 键名
     * @return string
     */
    public function getKey($key = "")
    {
        if (empty($key))
            return "{$this->_dbname}:{$this->_tablename}";
        return "{$this->_dbname}:{$this->_tablename}:{$key}";
    }
    /**
     * 查找缓存键关系孩子节点并加载要清理的缓存键
     * @param mixed $array 
     */
    private function loadViewRelatKeyFindChild(&$array)
    {
        /**
         * @var array $rarray 视图关联的数据表
         * @var string $vname 视图名称
         */
        foreach ($this->viewrelatkey as $vname => $rarray) {
            $isadd = false;
            foreach ($rarray as $tablename) {
                if (isset($this->keyarray[$tablename]) && $this->keyarray[$tablename] == 1) {
                    $isadd = true;
                    goto next;
                }
            }
            next:
            if ($isadd)
                $array[$vname] = 1;
        }
    }
    private function loadViewRelatKey()
    {
        $array = array();
        if (count($this->viewrelatkey)) {
            foreach (array_keys($this->keyarray) as $key) {
                if (!array_key_exists($key, $this->viewrelatkey))
                    continue;
                $chlidarray = $this->viewrelatkey[$key];
                foreach ($chlidarray as $dataarray) {
                    $array[$dataarray] = 1;
                }
            }
        }
        $this->loadViewRelatKeyFindChild($array);
        //合并两个数组
        $this->keyarray = array_merge($this->keyarray, $array);
    }
    /**
     * 事务性清理缓存
     */
    function transDel()
    {
        if (count($this->keyarray) > 0) {
            $this->del_Right();
            unset($this->keyarray);
            $this->keyarray = array();
        }
    }
    /**
     * 添加要管理的key
     * @param string $dbname 数据库名
     * @param string $tablename 表名
     * @param string $key 要添加的关键key,例如:(id:1)
     */
    public function addKey()
    {
        $this->keyarray[$this->getKey()] = 1;
    }
    /**
     * 根据每个key进行清除
     * @param string $nkey 
     * @return array
     */
    protected function clean($nkey)
    {
        return null;
    }
    private function del_abs($func)
    {
        if ($this->isEmptyWithConfig())
            return;
        $this->loadViewRelatKey();
        /**
         * 清理缓存
         * @var string $ekey
         */
        foreach (array_keys($this->keyarray) as $ekey) {
            $nkey = $func($ekey);
            $this->clean($nkey);
        }
    }
    /**
     * 将集合添加进
     * @param string $key 
     * @param array $array 
     */
    public function addArray($key, $array)
    {
        $this->set($key, $array);
    }
    /**
     * 添加进缓存
     * @param string $key 
     * @param array $array 
     */
    protected abstract function set($key, $array);
    /**
     * 根据键获取集合
     * @param string $key 键
     * @return array
     */
    public function getArray($key)
    {
        $array = $this->get($key);
        return $array;
    }
    /**
     * 获取数据的逻辑
     * @param string $key 键名
     * @return array
     */
    protected abstract function get($key);
    /**
     * 模糊删除,key右边模糊,例如:del("key*")
     */
    public function del_Right()
    {
        $this->del_abs(function ($ekey) {
            return $ekey . "*";
        });
    }
    /**
     * 模糊删除,key左边模糊,例如:del("*key")
     */
    public function del_Left()
    {
        $this->del_abs(function ($ekey) {
            return "*" . $ekey;
        });
    }
    /**
     * 模糊删除,key左右两边都是模糊,例如:del("*key*")
     */
    public function del_All()
    {
        $this->del_abs(function ($ekey) {
            return "*" . $ekey . "*";
        });
    }
    /**
     * 完全匹配删除,例如:del("key")
     */
    public function del()
    {
        $this->del_abs(function ($ekey) {
            return $ekey;
        });
    }
    /**
     * 关闭
     */
    public abstract function close();
    /**
     * 是否配置
     * @return bool
     */
    public function isEmptyWithConfig()
    {
        return false;
    }
    /**
     * 写入日志
     * @param mixed $data 数据
     * @param string|array $key 缓存key
     * @param string $opertype add:添加缓存,rm:删除缓存,get:获取缓存
     */
    protected function writelog($key, $data, $opertype)
    {
        $logdata = array(
            "key" => $key,
            "opertype" => $opertype
        );
        $tablekey = $this->getKey();
        \Mlog::info(json_encode($logdata, JSON_UNESCAPED_UNICODE));
    }
    /**
     * 刷新缓存
     */
    public abstract function flush();
    /**
     * 启用数据锁
     * @var bool
     */
    protected $lock_on;
    /**
     * 设置是否启用数据锁
     * @param bool $on true:启用,false:不启用
     */
    public function setLockOn($on)
    {
        $this->lock_on = (bool)$on;
    }
}
